#include <LiquidCrystal_I2C.h>  // 导入LCD1602 I2C驱动库
#include <Wire.h>               // 导入I2C通信库（LCD1602 I2C模式依赖）
#include <Keypad.h>             // 导入矩阵键盘库

// -------------------------- 硬件参数配置 --------------------------
const uint8_t LCD_ADDR = 0x27;  // LCD1602的I2C设备地址（常见0x27或0x3F）
const uint8_t LCD_COLS = 16;    // LCD1602列数（16列）
const uint8_t LCD_ROWS = 2;     // LCD1602行数（2行）

// 矩阵键盘配置（4行4列）
const byte KEYPAD_ROWS = 4;  // 键盘行数
const byte KEYPAD_COLS = 4;  // 键盘列数

// 键盘按键映射表（与实际按键位置对应）
char keyMap[KEYPAD_ROWS][KEYPAD_COLS] = {
  { '1', '2', '3', 'A' },  // A=加
  { '4', '5', '6', 'B' },  // B=减
  { '7', '8', '9', 'C' },  // C=乘
  { '*', '0', '#', 'D' }   // *=归零，#=等于，D=除
};

// 键盘行引脚（连接Arduino数字引脚）
byte rowPins[KEYPAD_ROWS] = { 11, 10, 9, 8 };
// 键盘列引脚（连接Arduino数字引脚）
byte colPins[KEYPAD_COLS] = { 7, 6, 5, 4 };

// -------------------------- 全局对象与变量 --------------------------
LiquidCrystal_I2C lcd(LCD_ADDR, LCD_COLS, LCD_ROWS);  // LCD1602对象实例化
Keypad customKeypad = Keypad(                         // 矩阵键盘对象实例化
  makeKeymap(keyMap),                                 // 按键映射表
  rowPins,                                            // 行引脚数组
  colPins,                                            // 列引脚数组
  KEYPAD_ROWS,                                        // 行数
  KEYPAD_COLS                                         // 列数
);

// 计算器核心变量
float num1 = 0.0;         // 第一个操作数
float num2 = 0.0;         // 第二个操作数
char operation = 0;       // 运算符（A=加/B=减/C=乘/D=除）
bool isSecondNum = false; // 是否正在输入第二个操作数
bool hasResult = false;   // 是否已经计算出结果（用于连续运算）
String inputStr = "";     // 新增：存储第一行输入的表达式（用于保持显示）

// -------------------------- 初始化函数 --------------------------
void setup() {
  lcd.init();               // 初始化LCD1602
  lcd.backlight();          // 开启LCD背光
  lcd.setCursor(0, 0);      // 设置初始光标位置（第0列，第0行）
  lcd.print("Calculator");  // 开机显示计算器标题
  lcd.setCursor(0, 1);      // 设置光标到第二行
  lcd.print("A:+ B:- C:* D:/"); // 显示操作说明
  delay(3000);              // 提示显示3秒
  lcd.clear();              // 清屏准备接收输入
  inputStr = "";            // 初始化输入字符串
}

// -------------------------- 溢出检测函数 --------------------------
// 检测float计算结果是否溢出（超出Arduino float的有效范围）
bool isOverflow(float result) {
  // Arduino的float是32位单精度，有效范围约±3.4×10^38
  const float MAX_FLOAT = 3.4e38;
  const float MIN_FLOAT = -3.4e38;
  return (result > MAX_FLOAT || result < MIN_FLOAT || isnan(result));
}

// -------------------------- 计算函数 --------------------------
float calculate() {
  float result = 0.0;
  switch(operation) {
    case 'A': result = num1 + num2; break;  // 加法
    case 'B': result = num1 - num2; break;  // 减法
    case 'C': result = num1 * num2; break;  // 乘法
    case 'D': 
      if (num2 != 0) {
        result = num1 / num2;  // 除法（避免除零）
      } else {
        lcd.setCursor(0, 1);  // 错误信息显示在第二行
        lcd.print("Error: Divide 0");
        delay(1500);
        clearCalculator();  // 除零错误，重置计算器
        result = 0.0;
      }
      break;
    default: break;
  }
  return result;
}

// -------------------------- 重置计算器函数 --------------------------
void clearCalculator() {
  num1 = 0.0;
  num2 = 0.0;
  operation = 0;
  isSecondNum = false;
  hasResult = false;
  inputStr = "";            // 清空输入字符串
  lcd.clear();
}

// -------------------------- 主循环函数 --------------------------
void loop() {
  char pressedKey = customKeypad.getKey();  // 获取按键输入（无按键时返回0）

  // 检测到有效按键输入
  if (pressedKey != 0) {
    // 1. 归零键（*）：重置所有状态
    if (pressedKey == '*') {
      clearCalculator();
      return;
    }

    // 如果已有结果，新输入时清空第二行
    if (hasResult) {
      lcd.setCursor(0, 1);
      lcd.print("                ");  // 清空第二行
      hasResult = false;
    }

    // 2. 数字键（0-9）：处理数字输入
    if (pressedKey >= '0' && pressedKey <= '9') {
      // 确保第一行不超出16字符
      if (inputStr.length() < 16) {
        inputStr += pressedKey;          // 保存到输入字符串
        lcd.setCursor(0, 0);             // 光标回到第一行开头
        lcd.print(inputStr);             // 显示完整输入
      }
      
      if (!isSecondNum && operation == 0) {
        // 输入第一个操作数
        num1 = num1 * 10 + (pressedKey - '0');
      } else {
        // 输入第二个操作数
        isSecondNum = true;
        num2 = num2 * 10 + (pressedKey - '0');
      }
      return;
    }

    // 3. 运算符键（A/B/C/D）：选择运算方式
    if (pressedKey == 'A' || pressedKey == 'B' || pressedKey == 'C' || pressedKey == 'D') {
      if (hasResult) {
        // 连续运算：以上次结果作为第一个操作数
        num1 = num2;  // 上次结果存在num2中
        num2 = 0.0;
        isSecondNum = false;
        inputStr = String(num1);  // 输入字符串更新为上次结果
        lcd.setCursor(0, 0);
        lcd.print(inputStr);
      }
      
      operation = pressedKey;  // 记录运算符
      isSecondNum = true;      // 准备输入第二个操作数
      
      // 转换为标准符号并添加到输入字符串
      char opSymbol = 0;
      switch(pressedKey) {
        case 'A': opSymbol = '+'; break;
        case 'B': opSymbol = '-'; break;
        case 'C': opSymbol = '*'; break;
        case 'D': opSymbol = '/'; break;
      }
      // 确保第一行不超出16字符
      if (inputStr.length() < 16) {
        inputStr += opSymbol;
        lcd.setCursor(0, 0);
        lcd.print(inputStr);
      }
      return;
    }

    // 4. 等于键（#）：执行计算并显示结果（第二行）
    if (pressedKey == '#' && operation != 0 && isSecondNum) {
      // 第一行添加等号
      if (inputStr.length() < 16) {
        inputStr += "=";
        lcd.setCursor(0, 0);
        lcd.print(inputStr);
      }
      
      float result = calculate();  // 执行计算
      
      // 排除除零错误的情况
      if (operation != 'D' || num2 != 0) {
        lcd.setCursor(0, 1);  // 结果显示在第二行
        
        // 检测溢出
        if (isOverflow(result)) {
          lcd.print("ERROR");  // 溢出显示ERROR
        } else {
          // 优化显示：整数不显示小数点，小数正常显示
          if (result == (int)result) {
            lcd.print((int)result);  // 整数显示
          } else {
            lcd.print(result, 4);    // 小数显示，保留4位
          }
        }
        
        num1 = result;         // 保存结果用于连续运算
        num2 = result;         // 临时存储结果
        hasResult = true;      // 标记已计算出结果
        isSecondNum = false;   // 重置第二个操作数状态
      }
      return;
    }
  }
}